package com.da.ioc.frame;

import com.da.ioc.frame.annotation.Bean;
import com.da.ioc.frame.annotation.Inject;
import com.da.ioc.frame.util.FileScan;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 容器(处理注解)
 */
public class MyIoc {

    //    配置类
    private final Class<?> configClass;

    //    配置类的包名的开始包名(com)
    private final String configClassPackageNameFirst;

    //    存放最初实例化的bean
    private final Map<String, Object> createMap = new ConcurrentHashMap<>();

    //    存放处理好的bean
    private final Map<String, Object> simpleBean = new ConcurrentHashMap<>();

    //    传入配置类
    public MyIoc(Class<?> configClass) {
        this.configClass = configClass;
        this.configClassPackageNameFirst = configClass.getPackage().getName().split("\\.")[0];
//        初始化
        init();
    }

    //    初始化方法
    private void init() {
//        获取启动类所在的包名(com.xx.xx)
        final String rootPackage = configClass.getPackage().getName();
//        使用工具类扫描所有的.class文件
        final FileScan fileScan = new FileScan(rootPackage);
//        遍历扫描出来的class文件
        fileScan.getList().forEach(this::handleClassFile);
//        处理createMap中存的对象
        handleBeanMap();
    }

    //    处理每个class文件
    private void handleClassFile(String file) {
//        获取class文件的加载路径
        String classFilePath = file.replaceAll("\\\\", "\\.");
//        截取class文件的加载路径
        classFilePath = classFilePath.substring(classFilePath.indexOf(configClassPackageNameFirst)); // 去掉前面的部分
        classFilePath = classFilePath.substring(0, classFilePath.indexOf(".class")); // 去掉.class
//        处理class文件
        loadClass(classFilePath);
    }

    //    处理class文件
    private void loadClass(String classFilePath) {
//        使用类加载器加载class文件
        try {
            final Class<?> loadClass = configClass.getClassLoader().loadClass(classFilePath);
//            判断类上有没有Bean注解
            if (loadClass.isAnnotationPresent(Bean.class)) {
//                获取bean注解里面的值
                String beanName = loadClass.getAnnotation(Bean.class).value();
//                处理bean的名字
                beanName = handleBeanName(beanName, loadClass);
//                通过无参构造实例化当前类
                final Object newInstance = loadClass.getConstructor().newInstance();
//                创建对应关系,存入map集合
                createMap.put(beanName, newInstance);
            }
        } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    //        处理bean名字的方法
    private String handleBeanName(String beanName, Class<?> loadClass) {
        final String className = loadClass.getName();
//        通过.分割字符串为数组
        final String[] split = className.split("\\.");
//        获取数组的最后的值(类名)
        String name = split[split.length - 1];
//        替换类名首字母为小写
        name = name.replace(String.valueOf(name.charAt(0)), String.valueOf(name.charAt(0)).toLowerCase());
        //        如果不传名字就把类名首字母小写当成bean的名字
        return "".equals(beanName) ? name : beanName;
    }

    //    处理createMap(依赖注入)
    private void handleBeanMap() {
//        遍历createMap
        createMap.forEach((key, beanDefine) -> {
//            处理Inject注解
            handleInject(beanDefine);
//            存入simpleBean中
            simpleBean.put(key, beanDefine);
        });
    }

    //    依赖注入
    private void handleInject(Object classType) {
        final Class<?> typeClass = classType.getClass();
//        遍历当前类上的所有属性
        for (Field field : typeClass.getDeclaredFields()) {
//        判断属性上有没有Inject注解
            if (field.isAnnotationPresent(Inject.class)) {
//                获取注解的值,如果没有就是属性的名字
                String value = field.getAnnotation(Inject.class).value();
                if ("".equals(value)) {
                    value = field.getName();
                }
//                取消当前属性权限检查
                field.setAccessible(true);
//                先判断有没有再从createMap中获取对应的beanDefine
                if (createMap.containsKey(value)) {
//                    取出value对应的实例
                    final Object o = createMap.get(value);
                    try {
//                        获取当前的属性是否已经有值
                        final Object fo = field.get(classType);
//                        如果没有值就注入
                        if (fo == null) {
//                        注入当前对象的值
                            field.set(classType, o);
                        }
//                        开启当前属性的权限检查
                        field.setAccessible(false);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                } else {
                    throw new RuntimeException("未定义bean => " + value);
                }
            }
        }
    }


    //    从处理好的simpleBean中返回bean
    public Object getBean(String beanName) {
        if (simpleBean.containsKey(beanName)) {
            return simpleBean.get(beanName);
        } else {
            throw new RuntimeException("未定义bean => " + beanName);
        }
    }

    //    从处理好的simpleBean中返回强转好的bean
    public <T> T getBean(String beanName, Class<T> clazz) {
        if (simpleBean.containsKey(beanName)) {
//            强转一下类型返回
            return (T) simpleBean.get(beanName);
        } else {
            throw new RuntimeException("未定义bean => " + beanName);
        }
    }

}
